#include	"gamehost.h"

int
main(int argc, char **argv)
{
	int			i, maxi, maxfd, listenfd, connfd, sockfd;
	int			contfd;
	int			nready, client[MAXCLIENTS];
	int			playerAvailable;	
	int			*ships;
	ssize_t			n;
	fd_set			rset, allset;
	char			buf[MAXLINE];
	socklen_t		clilen;
	struct sockaddr_in	cliaddr, servaddr, contaddr;
	
	playerfd = -1;
	playernumber = -1;
	playerAvailable = 0;

	listenfd = socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family      = AF_INET;
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	servaddr.sin_port        = htons(SERV_PORT);

	bind(listenfd, (SA *) &servaddr, sizeof(servaddr));

	listen(listenfd, LISTENQ);

	maxfd = listenfd;			/* initialize */
	maxi = -1;				/* index into client[] array */
	for (i = 0; i < FD_SETSIZE; i++)
		client[i] = -1;			/* -1 indicates available entry */
	FD_ZERO(&allset);
	FD_SET(listenfd, &allset);

/*connect to the controller*/

	contfd = socket(AF_INET, SOCK_STREAM, 0);

	bzero(&contaddr, sizeof(contaddr));
	contaddr.sin_family = AF_INET;
	contaddr.sin_port = htons(SERV_PORT);
	inet_pton(AF_INET, argv[1], &contaddr.sin_addr);

	connect(contfd, (SA *) &contaddr, sizeof(contaddr));

/*add controller's fd to all the tables*/

	for (i = 0; i < FD_SETSIZE; i++)
		if (client[i] < 0) {
			client[i] = contfd;	/* save descriptor */
			break;
		}
	if (i == FD_SETSIZE){
		perror("too many clients");
		exit(1);
	}
	FD_SET(contfd, &allset);

	if (connfd > maxfd)
		maxfd = connfd;			/* for select */
		if (i > maxi)
			maxi = i;

/*start listening for incoming messages*/

	for ( ; ; ) {
		rset = allset;		/* structure assignment */
		nready = select(maxfd+1, &rset, NULL, NULL, NULL);

		if (FD_ISSET(listenfd, &rset)) {	/* new client connection */
			clilen = sizeof(cliaddr);
			connfd = accept(listenfd, (SA *) &cliaddr, &clilen);
#ifdef	NOTDEF
			printf("new client: %s, port %d\n",
					inet_ntop(AF_INET, &cliaddr.sin_addr, 4, NULL),
					ntohs(cliaddr.sin_port));
#endif

			for (i = 0; i < FD_SETSIZE; i++)
				if (client[i] < 0) {
					client[i] = connfd;	/* save descriptor */
					break;
				}
			if (i == FD_SETSIZE){
				perror("too many clients");
				exit(1);
			}

			FD_SET(connfd, &allset);	/* add new descriptor to set */
			if (connfd > maxfd)
				maxfd = connfd;			/* for select */
			if (i > maxi)
				maxi = i;				/* max index in client[] array */

			if (--nready <= 0)
				continue;				/* no more readable descriptors */
		}

		for (i = 0; i <= maxi; i++) {	/* check all clients for data */
			if ( (sockfd = client[i]) < 0)
				continue;

			if (FD_ISSET(sockfd, &rset)) {
				if ( (n = readMsg(sockfd, buf)) <= 0) {
						/*4connection closed by client */
					close(sockfd);
					FD_CLR(sockfd, &allset);
					client[i] = -1;
				} else{
					if(n = inspectMsg(sockfd, buf) < 0){
						perror("inspectMsg");
						exit(1);
					}
				}

				if (--nready <= 0)
					break;		/* no more readable descriptors */
			}
		}
	}
}


int inspectMsg(int sockfd, char *msg)
{
/*this function is used to inspect the message that was received and act according to the message contents*/

	int n;
	int gameid;
	gameid = getGameId(msg);
	
	switch(msg[0]){

		case GAME_HOST_READY: 	/*NOP*/
			  		break;

		case GAME_HOST_JOIN_OK: getGameInfo(msg);
					break;

		case PLAYER_READY: 	sendPlayerReady(gameid, sendfd, msg);
					break;

		case TURN:		doSendTurn(gameid, playerfd);
					waitforaction();				
					doSendTurn(gameid, sendfd);
					break;

		case SHOOT:		checkwhosent(msg);
					if(playersent)
						sendShootResponse(msg, playerfd); 	
					else{				
						checkIfHit(msg);
						doSendShoot(gameid, sendfd, playernumber, msg);
					}
					break;

		case SHOOT_RESPONSE: 	/*NOP*/
					break;

		case PLAYER_QUIT: 	doPlayerQuit(gameid, playernumber, sendfd);
			  		break;

		case GAME_END: 		doGameEnd(gameid, playerfd);
					doGameEnd(gameid, sendfd);
					return 1; /*to indicate that the game host must shutdown*/

		default:  		return -1;
	}
	return 0;
}

int readMsg(int sockfd, char *msg)
{
/*this function is used to read the standard sized message, which size is defined by macro MSGSIZE. The message that is read is read to location pointed by msg*/

	size_t		nleft, n;
	ssize_t		nread;
	char 		*ptr;
	
	ptr = msg;
	n = MSGSIZE;
	nleft = n;

	while(nleft > 0){
		if(nread = read(sockfd, ptr, nleft) < 0){
			if(errno == EINTR)
				nread = 0;
			else 
				return -1;
		}
		else if(nread == 0)
			break;
		nleft -= nread;
		ptr += nread;
	}
	return (n - nleft);
}

int writeMsg(int sockfd, const char *msg)
{
/*this function is used to send the standard sized message to the socket defined by sockfd. Message size is defined by macro MSGSIZE.*/

	size_t 		nleft, n;
	ssize_t		nwritten;
	const char	*ptr;
	
	ptr = msg;
	n = MSGSIZE;
	nleft = MSGSIZE;

	while(nleft > 0){
		if((nwritten = write(sockfd, ptr, nleft)) <= 0){
			if(errno == EINTR)
				nwritten = 0;
			else 
				return -1;
		}
		nleft -= nwritten;
		ptr += nwritten;
	}
	return n;
}

int sendMsg(int msgid, int gameid, int sender, int sendfd, char **specs)
{
	char msg[MSGSIZE];
	int i, j;
	msg[0] = msgid;
	msg[1] = gameid;
	msg[2] = sender; /*in certain messages(GAME_HOST_JOIN_OK), this field is used to send other information*/

	for(i = 3; i < MSGSIZE; i++){
		msg[i] = specs[i - 3];
	}
	if(writeMsg(sendfd, msg) < 0)
		return -1;

}

int doSendTurn(int gameid, int sendfd){
	
	if(sendMsg(TURN, gameid, 0, sendfd, NULL) < 0){ /*sender is set to 0 to indicate that we are not interested in it's value in TURN messages*/

		perror("sendMsg");
		exit(1);
	}
	return 0;
}

int doSendShoot(int gameid, int sendfd, int sender){

	if(sendMsg(SHOOT, gameid, sender, sendfd, NULL) < 0){
		perror("sendMsg");
		exit(1);
	}
	return 0;
}

int getGameInfo(char *msg)
{
	int addrlen;
	struct sockaddr_in newsendaddr;
	gameid = msg[1];
	playernumber = msg[2];
	addrlen = msg[3];
	
}
	
void doGameEnd(int gameid, int sendfd){

	sendMsg(GAME_END, gameid, CONTROLLER, sendfd, NULL);
	exit(0);
}

int doPlayerQuit(int gameid, int sendfd, struct game *games){

	games[gameid].player_count--;

	if(games[gameid].player_count < 2){
		if((sendMsg(GAME_END, gameid, CONTROLLER, sendfd, NULL)) < 0)
			return -1;
	}
	return 0;
}

int doSendShoot(int gameid, int sendfd, int sender){

	if(sendMsg(SHOOT, gameid, sender, sendfd, NULL) < 0){
		perror("sendMsg");
		exit(1);
	}
	return 0;
}

int getGameId(char *msg){

	return msg[0];
}








